package com.doge.walknovel.business.reader

import android.util.LruCache
import com.doge.walknovel.api.*
import com.doge.walknovel.api.model.ChapterInfo
import com.doge.walknovel.utils.*
import java.io.*

object ChapterCacheHelper {
    private val novelChaptersDir: File get() {
        return File(appContext.externalCacheDir, "novelChapters").apply {
            if (!exists()) mkdirs()
        }
    }

    fun clearMemoryCache() = memoryCache.evictAll()
    private val memoryCache = LruCache<Int, ChapterInfo>(100)
    private val lock = Any()

    fun requireChapterInfo(chapterId: Int?, callback: (ChapterInfo) -> Unit = {}) {
        log("ChapterCacheHelper requireChapterInfo chapterId $chapterId")
        if (chapterId == null) return
        runOnIO {
            var invoked = false
            val memory = memoryCache.get(chapterId)
            if (memory != null) {
                invoked = true
                log("ChapterCacheHelper requireChapterInfo chapterId $chapterId fetch from memory")
                callback(memory)
            } else {
                val disk: String? = loadCache(chapterId)
                val cache: ChapterInfo? = fromJson(disk, ChapterInfo::class.java)
                if (cache != null) {
                    memoryCache.put(chapterId, cache)
                    invoked = true
                    log("ChapterCacheHelper requireChapterInfo chapterId $chapterId fetch from disk")
                    callback(cache)
                }
            }
            if (!invoked) {
                api
                    .novelChapterContent(chapterId)
                    .map(ApiResultMapTransformer())
                    .subscribe(object : ObserverAdapter<ChapterInfo>() {
                        override fun onNext(t: ChapterInfo) {
                            if (!t.locked) {
                                memoryCache.put(chapterId, t)
                                saveCache(chapterId, t)
                            }
                            log("ChapterCacheHelper requireChapterInfo chapterId $chapterId fetch from service locked ${t.locked}")
                            callback(t)
                        }

                        override fun onError(e: Throwable) = Unit
                    })
            }
        }
    }

    private fun saveCache(chapterId: Int, data: ChapterInfo) = runOnIO {
        synchronized(lock) {
            val file = File(novelChaptersDir, chapterId.toString())
            FileUtils.write(file, toJson(data))
        }
    }

    private fun loadCache(chapterId: Int): String? {
        require(!isMainThread())
        val file = File(novelChaptersDir, chapterId.toString())
        return FileUtils.read(file)
    }

}